#include "image3d.h"


Image3D::Image3D (char* fichier)
{

    QFile file(fichier);

    assert(file.open(QIODevice::ReadOnly | QIODevice::Text ));
    QTextStream flux(&file);

    QString line;
    line = flux.readLine();
    assert(!line.compare("PGM3D"));

    flux >> line;
    width = line.toUInt();

    flux >> line;
    height = line.toUInt();

    flux >> line;
    depth = line.toUInt();

    flux >> line;
    greyMax = line.toUInt();

    while (! flux.atEnd()) {
        flux >> line;
        image3D.push_back(line.toUShort() );
    }
    file.close();

    Ycouches = height-1;
}



unsigned Image3D::draw() {
    assert (!image3D.empty());
    float  alpha;
    float facAlpha = 10.0*(float)greyMax;

    // compteur de faces
    unsigned countF = 0;

    unsigned tmpW = width -1;
    unsigned tmpD = depth -1;


    for (unsigned i=1; i<tmpW; ++i)
        for (unsigned j=1; j<Ycouches; ++j)
            for (unsigned k=1; k<tmpD; ++k){
                alpha = (float)(image3D.at(k*width*height + j*width+i))/facAlpha;
                if ( alpha )
                    countF += drawCube(i,j,k, alpha);
                else
                    continue;
            }

    drawLayer();
    return countF;
}



unsigned Image3D::drawCube(unsigned x, unsigned y,
                           unsigned z, const float a) {

    const unsigned hw = width/2;
    const unsigned hh = height/2;
    const unsigned hd = depth/2;

    const unsigned short value = image3D.at(z*width*height + y*width+x);
    glColor4f(1.0, 1.0, 1.0, a);

    float i = (float)x-hw;
    float j = (float)y-hh;
    float k = (float)z-hd;

    unsigned count = 0;


    // face top
    if (!isConnected(value,x,y,z,TOP)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i  , j, k);
        glVertex3f(i+1, j, k);
        glVertex3f(i+1, j, k+1);
        glVertex3f(i  , j, k+1);
        glEnd();
    }

    // face bottom
    if (!isConnected(value,x,y,z,BOTTOM)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i  , j+1, k);
        glVertex3f(i+1, j+1, k);
        glVertex3f(i+1, j+1, k+1);
        glVertex3f(i  , j+1, k+1);
        glEnd();
    }

    // face avant
    if (!isConnected(value,x,y,z,FRONT)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i  , j  , k);
        glVertex3f(i+1, j  , k);
        glVertex3f(i+1, j+1, k);
        glVertex3d(i  , j+1, k);
        glEnd();
    }

    // face arriere
    if (!isConnected(value,x,y,z,BACK)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i  , j  , k+1);
        glVertex3f(i+1, j  , k+1);
        glVertex3f(i+1, j+1, k+1);
        glVertex3d(i  , j+1, k+1);
        glEnd();
    }


    // face leftgl
    if (!isConnected(value,x,y,z,LEFT)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i, j  , k);
        glVertex3f(i, j+1, k);
        glVertex3f(i, j+1, k+1);
        glVertex3d(i, j  , k+1);
        glEnd();
    }

    // face right
    if (!isConnected(value,x,y,z,RIGHT)) {
        ++count;
        glBegin(GL_QUADS);
        glVertex3f(i+1, j  , k);
        glVertex3f(i+1, j+1, k);
        glVertex3f(i+1, j+1, k+1);
        glVertex3d(i+1, j  , k+1);
        glEnd();
    }

    return count;

}




bool Image3D::isConnected(const unsigned short val,
                          const unsigned i,
                          const unsigned j,
                          const unsigned k,
                          const unsigned face) const {

    unsigned short voisin = -1;

    switch (face) {
    case TOP : {
        voisin = image3D.at(k*width*height + (j-1)*width+i);
        break;
    }
    case BOTTOM : {
        voisin = image3D.at(k*width*height + (j+1)*width+i);
        break;
    }
    case FRONT : {
        voisin = image3D.at((k-1)*width*height + j*width+i);
        break;
    }
    case BACK : {
        voisin = image3D.at((k+1)*width*height + j*width+i);
        break;
    }
    case LEFT : {
        voisin = image3D.at(k*width*height + j*width+i-1);
        break;
    }
    case RIGHT : {
        voisin = image3D.at(k*width*height + j*width+i+1);
        break;
    }
    }

    if (voisin != val || voisin==0)
        return false;
    return true;
}



void Image3D::addLayer() {
    if ( Ycouches<height )
        ++Ycouches;
}

void Image3D::removeLayer() {
    if ( Ycouches>1 )
        --Ycouches;
}



void Image3D::drawLayer() {

    const unsigned hw = width /2;
    const unsigned hh = height/2;
    const unsigned hd = depth /2;

    float i;
    float j;
    float k;
    float color;

    for (unsigned x=1; x<width-1; ++x)
        for (unsigned z=1; z<depth-1; ++z) {

            i = (float)x-hw;
            j = (float)Ycouches-hh;
            k = (float)z-hd;

            color = image3D.at(z*width*height + Ycouches*width+x);

            if ( color) {
                color /= (float)greyMax;
                glColor3f(color, color, color);
                glBegin(GL_QUADS);
                glVertex3f(i  , j, k);
                glVertex3f(i+1, j, k);
                glVertex3f(i+1, j, k+1);
                glVertex3f(i  , j, k+1);
                glEnd();
            }

        }
}



unsigned Image3D::getLayer() {
    return Ycouches;
}



std::ostream& operator << (std::ostream& os, const Image3D& im) {
    os << "width\t:\t" << im.width << std::endl;
    os << "height\t:\t" << im.height << std::endl;
    os << "depth\t:\t" << im.depth << std::endl;
    os << "greyMax\t:\t" << im.greyMax << std::endl;

    return os;
}



